package com.example.distributedlock.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * MyRedisLock
 *
 * @author lx
 * @date 2024-03-21 20:04
 */
@RestController
public class RedisLockController {
    @Autowired
    RedisTemplate<String, String> redisTemplate;

    @GetMapping("/redisLock")
    public String redisLock() throws InterruptedException {
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("redisLock", "111");
        if (lock) {
            //加锁成功  执行业务
            try {
                Object lx = redisTemplate.opsForValue().get("lx");
                Thread.sleep(2000);
                if (lx != null) {
                    int i = (int) lx - 1;
                    redisTemplate.opsForValue().set("lx", String.valueOf(i));
                    System.out.println(i);
                    return String.valueOf(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
//                todo 无法解决突然断电问题 断电则变为死锁
                redisTemplate.delete("redisLock");
            }
        } else {
            //todo 休眠100ms 继续占锁
            redisLock();
        }
        return String.valueOf(1000);
    }

    @GetMapping("/redisLock2")
    public String redisLock2() throws InterruptedException {
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("redisLock2", "111");
        if (lock) {
            try {
//                设置过期时间
//                todo 但若是在此处断电还是会造成死锁问题
                redisTemplate.expire("redisLock2", 30, TimeUnit.SECONDS);
                Object lx = redisTemplate.opsForValue().get("lx");
                Thread.sleep(2000);
                if (lx != null) {
                    int i = (int) lx - 1;
                    redisTemplate.opsForValue().set("lx", String.valueOf(i));
                    System.out.println(i);
                    return String.valueOf(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
//                todo 无法解决突然断电问题 断电则变为死锁
                redisTemplate.delete("redisLock2");
            }
        }
        return String.valueOf(1000);
    }

    @GetMapping("/redisLock3")// 无法解决执行业务时间超过10秒的问题
    public String redisLock3() throws InterruptedException {
        //todo 原子操作
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("redisLock3", "111", 10, TimeUnit.SECONDS);
        if (lock) {
            try {
                Object lx = redisTemplate.opsForValue().get("lx");
                Thread.sleep(30000);//业务时间过长
                if (lx != null) {
                    int i = (int) lx - 1;
                    redisTemplate.opsForValue().set("lx", String.valueOf(i));
                    System.out.println(i);
                    return String.valueOf(i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                redisTemplate.delete("redisLock3");
            }
        }
        return String.valueOf(1000);
    }

    @GetMapping("/redisLock4")// todo 只删除自己的锁
    public String redisLock4() throws InterruptedException {
        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("redisLock4", uuid, 10, TimeUnit.SECONDS);
        if (lock) {
            try {
                Thread.sleep(9000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // todo 请求获取到redisLock4锁刚好过期 也会删除别人的锁
                // 所以获取锁的值和删除锁也必须是原子操作  todo 使用lua脚本完成
                String redisLock4 = redisTemplate.opsForValue().get("redisLock4");
                if (uuid.equals(redisLock4)) {
                    redisTemplate.delete("redisLock4");
                }
            }
        }
        return String.valueOf(1000);
    }
}
